#include "ResourceLoaderBridge.h"

#include "base/message_loop.h"
#include "base/ref_counted.h"
#include "base/time.h"
#include "base/thread.h"
#include "base/waitable_event.h"
#include "base/timer.h"
#include "net/base/cookie_monster.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_util.h"
#include "net/base/upload_data.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "net/proxy/proxy_service.h"
#include "net/url_request/url_request.h"
#include "webkit/glue/resource_loader_bridge.h"
#include "RequestContext.h"

using webkit_glue::ResourceLoaderBridge;
using net::HttpResponseHeaders;

MessageLoop* coreProxyMsgLoop = NULL;

namespace
{
	URLRequestContext* request_context = NULL;
	base::Thread* io_thread = NULL;

	class IOThread : public base::Thread
	{
	public:
		IOThread() : base::Thread("IOThread")
		{
		}

		~IOThread()
		{
			Stop();
		}

		virtual void CleanUp()
		{
			if (request_context)
			{
				request_context->Release();
				request_context = NULL;
			}
		}
	};

	bool EnsureIOThread()
	{
		if (io_thread)
			return true;

		if (!request_context)
			SimpleResourceLoaderBridge::Init(NULL);

		io_thread = new IOThread();
		base::Thread::Options options;
		options.message_loop_type = MessageLoop::TYPE_IO;
		return io_thread->StartWithOptions(options);
	}

	struct RequestParams
	{
		std::string method;
		GURL url;
		GURL policy_url;
		GURL referrer;
		std::string headers;
		int load_flags;
		scoped_refptr<net::UploadData> upload;
	};

	static const int kUpdateUploadProgressIntervalMsec = 100;
	class RequestProxy : public URLRequest::Delegate,
		public base::RefCountedThreadSafe<RequestProxy>
	{
	public:
		RequestProxy()
			: buf_(new net::IOBuffer(kDataSize)),
			last_upload_position_(0)
		{

		}

		virtual ~RequestProxy()
		{
			DCHECK(!request_.get() ||
				MessageLoop::current() == io_thread->message_loop());
		}

		void DropPeer()
		{
			peer_ = NULL;
		}

		void Start(ResourceLoaderBridge::Peer* peer, RequestParams* params)
		{
			peer_ = peer;
			owner_loop_ = coreProxyMsgLoop;
			io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
				this, &RequestProxy::AsyncStart, params));
		}

		void Cancel()
		{
			io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
				this, &RequestProxy::AsyncCancel));
		}

	protected:
		void NotifyReceivedRedirect(const GURL& new_url,
			const ResourceLoaderBridge::ResourceInfo& info)
		{
			if (peer_)
				peer_->OnReceivedRedirect(new_url, info);
		}

		void NotifyReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info,
									bool content_filtered)
		{
			if (peer_)
				peer_->OnReceivedResponse(info, content_filtered);
		}

		void NotifyReceivedData(int bytes_read)
		{
			if (!peer_)
				return;

			scoped_array<char> buf_copy(new char[bytes_read]);
			memcpy(buf_copy.get(), buf_->data(), bytes_read);

			io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
																			 this, &RequestProxy::AsyncReadData));
			peer_->OnReceivedData(buf_copy.get(), bytes_read);
		}

		void NotifyCompletedRequest(const URLRequestStatus& status,
									const std::string& serurity_info)
		{
			if (peer_)
			{
				peer_->OnCompletedRequest(status, security_info);
				DropPeer();
			}
		}

		void NotifyUploadProgress(uint64 position, uint64 size)
		{
			if (peer_)
				peer_->OnUploadProgress(position, size);
		}

		void AsyncStart(RequestParams* params)
		{
			request_.reset(new URLRequest(params->url, this));
			request_->set_method(params->method);
			request_->set_first_party_for_cookies(params->policy_url);
			request_->set_referrer(params->referrer.spec());
			request_->SetExtraRequestHeaders(params->headers);
			request_->set_load_flags(params->load_flags);
			request_->set_upload(params->upload.get());
			request_->set_context(request_context);
			request_->Start();

			if (request_->has_upload() &&
				params->load_flags & net::LOAD_ENABLE_UPLOAD_PROGRESS) 
			{
				upload_progress_timer_.Start(
					base::TimeDelta::FromMilliseconds(kUpdateUploadProgressIntervalMsec),
											 this, &RequestProxy::MaybeUpdateUploadProgress);
			}

			delete params;
		}

		void AsyncCancel()
		{
			if (!request_.get())
				return;

			request_->Cancel();
			Done();
		}

		void AsyncReadData()
		{
			if (!request_.get())
				return;

			if (request_->status().is_success())
			{
				int bytes_read;
				if (request_->Read(buf_, kDataSize, &bytes_read) && bytes_read)
				{
					OnReceivedData(bytes_read);
				}
				else if (!request_->status().is_io_pending())
				{
					Done();
				}
			}
			else
			{
				Done();
			}
		}

		virtual void OnReceivedRedirect(const GURL& new_url,
			const ResourceLoaderBridge::ResponseInfo& responseInfo,
				bool* defer_redirect)
		{
			*defer_redirect = true;
			owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
				this, &RequestProxy::NotifyReceivedRedirect, new_url, responseInfo));
		}

		virtual void OnReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info,
			bool content_filtered)
		{
			owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
				this, &RequestProxy::NotifyReceivedResponse, info, content_filtered));
		}

		virtual void OnReceivedData(int bytes_read)
		{
			owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
															   this, &RequestProxy::NotifyReceivedData, bytes_read));
		}

		virtual void OnCompletedRequest(const URLRequestStatus& status,
			const std::string& security_info)
		{
			owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
				this, &RequestProxy::NotifyCompletedRequest, status, security_info));
		}

		virtual	void OnResponseStarted(URLRequest* request)
		{
			if (request->status().is_success())
			{
				ResourceLoaderBridge::ResponseInfo info;
				info.request_time = request->request_time();
				info.response_time = request->response_time();
				info.headers = request->response_headers();
				request->GetMimeType(&info.mime_type);
				request->GetCharset(&info.charset);
				info.content_length = request->GetExpectedContentSize();
				OnReceivedResponse(info, false);
				AsyncReadData();
			}
			else
			{
				Done();
			}
		}

		virtual void OnReadCompleted(URLRequest* request, int bytes_read)
		{
			if (request->status().is_success() && bytes_read > 0)
			{
				OnReceivedData(bytes_read);
			}
			else
			{
				Done();
			}
		}

		void Done()
		{
			if (upload_progress_timer_.IsRunning())
			{
				MaybeUpdateUploadProgress();
				upload_progress_timer_.Stop();
			}
			DCHECK(request_.get());
			OnCompletedRequest(request_->status(), std::string());
			request_.reset();
		}

		void MaybeUpdateUploadProgress()
		{
			uint64 size = request_->get_upload()->GetContentLength();
			uint64 position = request_->GetUploadProgress();
			if (position == last_upload_position_)
				return;

			const uint64 kHalfPercentIncrements = 200;
			const base::TimeDelta kOneSecond = base::TimeDelta::FromMilliseconds(1000);

			uint64 amt_since_last = position - last_upload_position_;
			base::TimeDelta time_since_last = base::TimeTicks::Now() -
				last_upload_ticks_;

			bool is_finished = (size == position);
			bool enough_new_progress = (amt_since_last > (size /
														kHalfPercentIncrements));
			bool too_much_time_elapsed = time_since_last > kOneSecond;

			if (is_finished || enough_new_progress || too_much_time_elapsed)
			{
				onwer_loop_->PostTask(FROM_HERE, NewRunnableMethod(
					this, &RequestProxy::NotifyUploadProgress, position, size));
				last_upload_ticks = base::TimeTicks::Now();
				last_upload_position_ = position;
			}
		}

		scoped_ptr<URLRequest> request_;

		static const int kDataSize = 16 * 1024;

		scoped_refptr<net::IOBuffer> buf_;

		MessageLoop* owner_loop_;

		ResourceLoaderBridge::Peer* peer_;

		base::RepeatingTimer<RequestProxy> upload_progress_timer_;

		uint64 last_update_position_;
		base::TimeTicks last_upload_ticks_;
	};

	class SyncRequestProxy : public RequestProxy
	{
	public:
		explicit SyncRequestProxy(ResourceLoaderBridge::SyncLoadResponse* result)
			: result_(result), event_(true, false)
		{
		}

		void WaitForCompletion()
		{
			if (!event_.Wait())
				NOTREACHED();
		}

		virtual void OnReceivedRedirect(const GURL& new_url)
		{
			result_->url = new_url;
		}

		virtual void OnReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info,
										bool content_filtered)
		{
			*static_cast<ResourceLoaderBridge::ResponseInfo*>(result_) = info;
		}

		virtual void OnReceivedData(int bytes_read)
		{
			result_->data.append(buf_->data(), bytes_read);
			AsyncReadData();
		}

		virtual void OnCompletedRequest(const URLRequestStatus& status,
										const std::string& security_info)
		{
			result_->status = status;
			event_.Signal();
		}

	private:
		ResourceLoaderBridge::SyncLoadResponse* result_;
		base::WaitableEvent event_;
	};

	class ResourceLoaderBridgeImpl : public ResourceLoaderBridge
	{
	public:
		ResourceLoaderBridgeImpl(const std::string& method,
								 const GURL& url,
								 const GURL& policy_url,
								 const GURL& referrer,
								 const std::string& headers,
								 int load_flags)
			: params_(new RequestParams),
			  proxy_(NULL),
		{
			params_->method = method;
			params_->url = url;
			params_->policy_url = policy_url;
			params_->referrer = referrer;
			params_->headers = headers;
			params_->load_flags = load_flags;
		}

		virtual ~ResourceLoaderBridgeImpl()
		{
			if (proxy_)
			{
				proxy_->DropPeer();
				if (io_thread)
					io_thread->message_loop()->ReleaseSoon(FROM_HERE, proxy_);
			}
		}

		
	};
}
